import { injectable, inject } from 'inversify';
import WebSocket from 'ws';
import { ISessionService } from '../../application/interfaces/ISessionService';
import { IMessageService } from '../../application/interfaces/IMessageService';
import { IAgentService } from '../../application/interfaces/IAgentService';
import { IAuthService } from '../../application/interfaces/IAuthService';
import { IUserService } from '../../application/interfaces/IUserService';
import { LoggerService } from '../../infrastructure/logging/logger.service';
import { wsEvents, WsEventType } from '../../domain/events/ws.events';
import { TYPES } from '../../infrastructure/ioc/types';
import { WSMessage } from './ws.types';
import { Session } from '../../domain/entities/session.entity';
import { Message } from '../../domain/entities/message.entity';
import { User } from '../../domain/entities/user.entity';

interface WsClient {
  ws: WebSocket;
  clientId: string;
  userId?: string;
  authenticated: boolean;
}

@injectable()
export class WsHandler {
  private clients: Map<string, WsClient> = new Map();
  private userIdToClientId: Map<string, string> = new Map();

  constructor(
    @inject(TYPES.SessionService) private sessionService: ISessionService,
    @inject(TYPES.MessageService) private messageService: IMessageService,
    @inject(TYPES.AgentService) private agentService: IAgentService,
    @inject(TYPES.AuthService) private authService: IAuthService,
    @inject(TYPES.UserService) private userService: IUserService,
    @inject(TYPES.LoggerService) private logger: LoggerService
  ) {}

  handleConnection(ws: WebSocket) {
    const clientId = this.generateClientId();
    this.clients.set(clientId, { 
      ws, 
      clientId, 
      authenticated: false 
    });

    ws.on('message', (data) => this.handleMessage(clientId, data));
    ws.on('close', () => this.handleDisconnect(clientId));
    ws.send(JSON.stringify({ type: wsEvents.CONNECT, payload: { clientId } }));
  }

  private async handleMessage(clientId: string, data: WebSocket.RawData) {
    const client = this.clients.get(clientId);
    if (!client) {
      this.logger.error(`Client ${clientId} not found`);
      return;
    }

    try {
      const message: WSMessage = JSON.parse(data.toString());
      const { ws } = client;

      // 身份验证处理
      if (message.type === wsEvents.AUTH_REQUEST) {
        await this.handleAuthentication(client, message);
        return;
      }

      // 注册请求处理
      if (message.type === wsEvents.REGISTER_REQUEST) {
        await this.handleRegistration(client, message);
        return;
      }

      // 其他消息类型需要身份验证
      if (!client.authenticated && message.type !== wsEvents.CONNECT) {
        ws.send(JSON.stringify({
          type: wsEvents.ERROR,
          payload: { error: 'Authentication required' }
        }));
        return;
      }

      const chatId = message.payload?.chatId || message.sessionId;
      const senderId = client.userId || message.payload?.message?.sender || message.senderId;
      const messageId = message.payload?.message?.id || message.payload?.messageId;

      switch (message.type) {
        case wsEvents.JOIN_CHAT:
          await this.handleJoinChat(client, chatId!, senderId!);
          break;

        case wsEvents.SEND_MESSAGE:
          await this.handleSendMessage(client, chatId!, senderId!, messageId, message.payload?.message?.content || '');
          break;

        case wsEvents.TYPING:
          await this.handleTyping(client, chatId!, senderId!, message.payload?.isTyping || false);
          break;

        case wsEvents.GET_CHAT_LIST:
          if (!senderId) throw new Error('Missing senderId');
          await this.handleGetChatList(client, senderId);
          break;

        default:
          this.logger.warn(`Unsupported event type: ${message.type}`);
          ws.send(JSON.stringify({ type: wsEvents.ERROR, payload: { error: `Unsupported event: ${message.type}` } }));
      }
    } catch (error: any) {
      this.logger.error(`Message handling error: ${error.message}`);
      client.ws.send(JSON.stringify({ type: wsEvents.ERROR, payload: { error: error.message } }));
    }
  }

  private async handleAuthentication(client: WsClient, message: WSMessage) {
    const { ws } = client;
    try {
      // 使用用户名/密码登录
      if (message.payload?.username && message.payload?.password) {
        const loginResult = await this.authService.login(
          message.payload.username,
          message.payload.password
        );

        // 认证成功
        client.authenticated = true;
        client.userId = loginResult.user.id;
        this.userIdToClientId.set(loginResult.user.id, client.clientId);

        // 更新用户状态为在线
        await this.userService.setUserStatus(loginResult.user.id, 'online');

        // 发送认证成功响应
        ws.send(JSON.stringify({
          type: wsEvents.AUTH_SUCCESS,
          payload: {
            user: {
              id: loginResult.user.id,
              username: loginResult.user.username,
              email: loginResult.user.email,
              role: loginResult.user.role,
              status: loginResult.user.status
            },
            accessToken: loginResult.accessToken,
            refreshToken: loginResult.refreshToken,
            timestamp: new Date().toISOString()
          }
        }));
        return;
      }

      // 使用令牌验证
      if (message.payload?.token) {
        const user = await this.authService.verifyToken(message.payload.token);
        if (!user) {
          throw new Error('Invalid or expired token');
        }

        // 认证成功
        client.authenticated = true;
        client.userId = user.id;
        this.userIdToClientId.set(user.id, client.clientId);

        // 更新用户状态为在线
        await this.userService.setUserStatus(user.id, 'online');

        // 发送认证成功响应
        ws.send(JSON.stringify({
          type: wsEvents.AUTH_SUCCESS,
          payload: {
            user: {
              id: user.id,
              username: user.username,
              email: user.email,
              role: user.role,
              status: user.status
            },
            timestamp: new Date().toISOString()
          }
        }));
        return;
      }

      // 如果没有提供认证信息
      ws.send(JSON.stringify({
        type: wsEvents.AUTH_FAILURE,
        payload: { 
          error: 'Missing authentication credentials',
          timestamp: new Date().toISOString()
        }
      }));

    } catch (error: any) {
      this.logger.error(`Authentication error: ${error.message}`);
      ws.send(JSON.stringify({
        type: wsEvents.AUTH_FAILURE,
        payload: { 
          error: error.message,
          timestamp: new Date().toISOString()
        }
      }));
    }
  }

  private async handleRegistration(client: WsClient, message: WSMessage) {
    const { ws } = client;
    try {
      if (!message.payload?.username || !message.payload?.password) {
        throw new Error('Username and password are required');
      }

      const user = await this.authService.register(
        message.payload.username,
        message.payload.password,
        message.payload.email
      );

      ws.send(JSON.stringify({
        type: wsEvents.REGISTER_SUCCESS,
        payload: {
          user: {
            id: user.id,
            username: user.username,
            email: user.email,
            role: user.role,
            status: user.status
          },
          timestamp: new Date().toISOString()
        }
      }));
    } catch (error: any) {
      this.logger.error(`Registration error: ${error.message}`);
      ws.send(JSON.stringify({
        type: wsEvents.REGISTER_FAILURE,
        payload: { 
          error: error.message,
          timestamp: new Date().toISOString()
        }
      }));
    }
  }

  private async handleJoinChat(client: WsClient, chatId: string, senderId: string) {
    const { ws } = client;
    let session = await this.sessionService.getSession(chatId);

    // 检查权限 - 示例：只允许参与者和管理员访问会话
    if (session && !(await this.canAccessSession(client, session))) {
      ws.send(JSON.stringify({
        type: wsEvents.ERROR,
        payload: { error: 'You do not have permission to join this chat' }
      }));
      return;
    }

    if (!session) {
      session = await this.sessionService.createSession([senderId]);
    } else if (!session.participants.includes(senderId)) {
      session.participants.push(senderId);
      await this.sessionService.updateSession(session);
    }

    const messages = await this.messageService.getChatHistory(chatId);
    ws.send(JSON.stringify({
      type: wsEvents.CHAT_HISTORY,
      payload: {
        chatId,
        messages: messages.map((msg: Message) => ({
          id: msg.id,
          type: 'user',
          sender: msg.senderId,
          content: msg.content,
          time: msg.timestamp.toISOString()
        }))
      }
    }));
  }

  private async handleSendMessage(client: WsClient, chatId: string, senderId: string, messageId: string | undefined, content: string) {
    // 1. 确认消息送达
    client.ws.send(JSON.stringify({
      type: wsEvents.MESSAGE_DELIVERED,
      payload: { messageId, chatId, timestamp: new Date().toISOString() }
    }));

    client.ws.send(JSON.stringify({
      type: wsEvents.MESSAGE_RECEIVED,
      payload: { chatId, messageId, status: 'received', statusText: '服务器已接收消息', timestamp: new Date().toISOString() }
    }));

    client.ws.send(JSON.stringify({
      type: wsEvents.MESSAGE_ANALYZING,
      payload: { chatId, messageId, status: 'analyzing', statusText: '正在分析您的问题', timestamp: new Date().toISOString() }
    }));

    const response = await this.agentService.processMessage(content);
    const savedMessage = await this.messageService.sendMessage(chatId, senderId, content);

    // 6. 广播消息
    const session = await this.sessionService.getSession(chatId);
    if (session) {
      this.broadcastMessage(session, savedMessage);
    }

    client.ws.send(JSON.stringify({
      type: wsEvents.NEW_MESSAGE,
      payload: {
        chatId,
        message: {
          id: `ai-${Date.now()}`,
          type: 'system',
          sender: 'AI-Agent',
          content: response,
          time: new Date().toISOString()
        }
      }
    }));

    client.ws.send(JSON.stringify({
      type: wsEvents.MESSAGE_COMPLETED,
      payload: { chatId, messageId, status: 'completed', statusText: '处理完成', timestamp: new Date().toISOString() }
    }));
  }

  private async handleTyping(client: WsClient, chatId: string, senderId: string, isTyping: boolean) {
    const session = await this.sessionService.getSession(chatId);
    
    // 检查权限
    if (session && !(await this.canAccessSession(client, session))) {
      return;
    }

    if (session) {
      const eventType: WsEventType = isTyping ? wsEvents.TYPING_START : wsEvents.TYPING_END;
      this.broadcast(session, {
        type: eventType,
        payload: { chatId, sender: senderId }
      });
    }
  }

  private async handleGetChatList(client: WsClient, userId: string) {
    const sessions = await this.sessionService.getUserSessions(userId);
    const chats = await Promise.all(sessions.map(async (session) => {
      const messages = await this.messageService.getChatHistory(session.id);
      const latestMessage = messages.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())[0];
      return {
        id: session.id,
        title: session.participants.length === 1 ? 'AI Chat' : session.participants.join(', '),
        preview: latestMessage ? latestMessage.content.slice(0, 50) : '',
        time: latestMessage ? this.formatTime(latestMessage.timestamp) : this.formatTime(session.createdAt),
        type: session.agentId ? 'ai-robot' : 'default'
      };
    }));

    client.ws.send(JSON.stringify({
      type: wsEvents.CHAT_LIST,
      payload: { chats }
    }));
  }

  private formatTime(date: Date): string {
    const now = new Date();
    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const yesterday = new Date(today);
    yesterday.setDate(yesterday.getDate() - 1);

    if (date >= today) {
      return `今天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
    } else if (date >= yesterday) {
      return `昨天 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
    } else {
      return `${date.getMonth() + 1}月${date.getDate()}日 ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}`;
    }
  }

  private broadcast(session: Session, message: { type: WsEventType; payload: any }) {
    session.participants.forEach(userId => {
      const clientId = this.userIdToClientId.get(userId);
      if (clientId) {
        const client = this.clients.get(clientId);
        if (client && client.ws.readyState === WebSocket.OPEN) {
          client.ws.send(JSON.stringify(message));
        }
      }
    });
  }

  private broadcastMessage(session: Session, message: Message) {
    const msg = {
      type: wsEvents.NEW_MESSAGE,
      payload: {
        chatId: message.sessionId,
        message: {
          id: message.id,
          type: 'user',
          sender: message.senderId,
          content: message.content,
          time: message.timestamp.toISOString()
        }
      }
    };
    this.broadcast(session, msg);
  }

  private handleDisconnect(clientId: string) {
    const client = this.clients.get(clientId);
    if (client && client.userId) {
      // 更新用户状态为离线
      this.userService.setUserStatus(client.userId, 'offline').catch(error => {
        this.logger.error(`Error setting user offline: ${error.message}`);
      });
      
      // 清理用户ID映射
      this.userIdToClientId.delete(client.userId);
    }
    
    this.clients.delete(clientId);
    this.logger.info(`Client ${clientId} disconnected`);
  }

  private async canAccessSession(client: WsClient, session: Session): Promise<boolean> {
    if (!client.userId) return false;
    
    // 检查用户是否是会话参与者
    const isParticipant = session.participants.includes(client.userId);
    if (isParticipant) return true;
    
    // 如果不是参与者，尝试获取用户并检查是否是管理员
    return await this.checkAdminStatus(client.userId);
  }

  private async checkAdminStatus(userId: string): Promise<boolean> {
    try {
      const user = await this.userService.findById(userId);
      return user?.role === 'admin';
    } catch (error) {
      return false;
    }
  }

  private generateClientId(): string {
    return Math.random().toString(36).substr(2, 9);
  }
}